package gbench.sandbox.matrix.algebra;

import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import org.junit.jupiter.api.Test;
public class JunitMyMatrix {
    public static class Tuple2<T,U>{
        public Tuple2(T t,U u){
            this._1 = t;
            this._2 = u;
        }
        
        public T _1() {return _1;}
        public U _2() {return _2;}
        
        public <V> Collector<Tuple2<T,U>,List<Tuple2<T,U>>,V> kvpclc(Function<List<Tuple2<T,U>>,V> mapper){
           return Collector.of(()->new LinkedList<Tuple2<T,U>>(),List::add, (aa,bb)->{aa.addAll(bb);return aa;},mapper );
        }
        
        private T _1;
        private U _2;
    }
    
    public static <T1,U1>  Tuple2<T1,U1> TUP2(T1 t,U1 u) {
        return new Tuple2<>(t,u);
    }
    
    public static <T,U>  Stream<Tuple2<T,U>> zip(T[] aa,U[] bb) {
        final var asize = aa.length;
        final var bsize = bb.length;
        final int n = Math.max(asize,bsize);
        return Stream.iterate(0,i->i<n,i->i+1).map(i->TUP2(aa[i%asize],bb[i%bsize]));
    }
    
    @SafeVarargs
    public static <T>  T[] A(T...tt){
        return tt;
    }
    
    @SafeVarargs
    public static <T>  List<T> L(T...tt){
        return Arrays.asList(tt);
    }
    
    public <T extends Number,U extends Number> Double dot(T[] aa,U[] bb){
       return zip(aa,bb).map((tup)->tup._1.doubleValue()*tup._2.doubleValue()).reduce((a,b)->a+b).get();
    }
    
    public <T extends Number,U extends Number> Double[] add(T[] aa,U[] bb){
        return zip(aa,bb).map((tup)->tup._1.doubleValue()+tup._2.doubleValue()).toArray(Double[]::new);
    }
    
    public <T extends Number,U extends Number> Double[] sub(T[] aa,U[] bb){
        return zip(aa,bb).map((tup)->tup._1.doubleValue()-tup._2.doubleValue()).toArray(Double[]::new);
    }
    
    public <T extends Number,U extends Number> Double[] mul(T[] aa,U[] bb){
        return zip(aa,bb).map((tup)->tup._1.doubleValue()*tup._2.doubleValue()).toArray(Double[]::new);
    }
    
    public <T extends Number,U extends Number> Double[] mul(T[] aa,U b){
        return zip(aa,A(b)).map((tup)->tup._1.doubleValue()*tup._2.doubleValue()).toArray(Double[]::new);
    }
    
    @SuppressWarnings("unchecked")
    public <T extends Number> List<T[]> schmidt(List<T[]> alphas){
        List<T[]> betas  = new LinkedList<T[]>();
        final var eps = 1e-10;
        Predicate<T[]> is_zero = aa->Arrays.stream(aa).allMatch(a->Math.abs(a.doubleValue())<eps);
        alphas.forEach(alpha->{
            final var t = (T[])betas.stream().map(beta->mul(beta,-dot(alpha,beta)/dot(beta,beta)))
                .reduce(this::add).map(e->add(alpha,e)).orElse((Double[])alpha);
            if(!is_zero.test(t))betas.add(t);
        });
        return betas;
    }
    
    @Test
    public void foo() {
       final var aa =  A(1d,0d,1d);
       final var bb =  A(0d,1d,0d);
       System.out.println(dot(aa,bb));
       var cc = L(aa,bb,aa);
       schmidt(cc).forEach(e->{
           System.out.println(Arrays.asList(e));
       });
    }
}
